//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "dll_symbol.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sched.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>

#define STACK_SIZE 4*1024*1024
dll_symbol_list *g_dllSymbolListHead = NULL;

int dll_symbol_list_add_head(dll_symbol_list **ppHead, dll_symbol_list *pNode)
{
    if (NULL == pNode)
        return -1;

    pNode->next = *ppHead;
    *ppHead = pNode;

    return 0;
}

int dll_symbol_list_delete_head(dll_symbol_list **ppHead)
{
    dll_symbol_list *headSymbol = *ppHead;

    if (*ppHead == NULL)
        return -1;

    *ppHead = (*ppHead)->next;

    free(headSymbol->data);
    free(headSymbol);

    return 0;
}

int dll_symbol_list_delete_all(dll_symbol_list **ppHead)
{
    dll_symbol_list *headSymbol = NULL;

    while (*ppHead) {
        headSymbol = *ppHead;
        *ppHead = (*ppHead)->next;
        free(headSymbol->data);
        free(headSymbol);
    }

    return 0;
}

int GetDllInfoThread(void *pArg);

typedef struct
{
    int m_iThreadId;
    int m_fd[2];
}DebugResource;

DebugResource g_debugResource;

int CreateGetDllInfo(int *outfd)
{
    int fd[2];
    char *pStack;
    int tid;

    g_debugResource.m_iThreadId = 0;
    g_debugResource.m_fd[0]     = 0;
    g_debugResource.m_fd[1]     = 0;


    pStack = (char *)malloc(STACK_SIZE);
    if(NULL == pStack){
        printf("Error: CreateGetDllInfo malloc failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);

        return 1;
    }

    if(pipe(fd) < 0){
        printf("Error: CreateGetDllInfo pipe failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        free(pStack);
        return 1;
    }

    tid = clone(GetDllInfoThread,
        pStack + STACK_SIZE - 1,
        CLONE_FS|CLONE_FILES|CLONE_VM|CLONE_SIGHAND,//|SIGCHLD,
        (void *)fd[0]);
    if(-1 == tid){
        printf("Error: Create GetDllInfoThread Thread failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        free(pStack);
        close(fd[0]);
        close(fd[1]);
        return 1;
    }

    *outfd = fd[1];

    g_debugResource.m_iThreadId = tid;
    g_debugResource.m_fd[0]     = fd[0];
    g_debugResource.m_fd[1]     = fd[1];

    return 0;
}

void DestroyDllInfo()
{
    if(0 != g_debugResource.m_iThreadId){
        kill(g_debugResource.m_iThreadId, SIGKILL);
    }
    if(0 != g_debugResource.m_iThreadId){
        close(g_debugResource.m_fd[0]);
    }
    if(0 != g_debugResource.m_iThreadId){
        close(g_debugResource.m_fd[1]);
    }

    g_debugResource.m_iThreadId = 0;
    g_debugResource.m_fd[0]     = 0;
    g_debugResource.m_fd[1]     = 0;
}

int GetDllSymbol(int fd, char *pBuf)
{
    char c;
    int ret;
    //todo:just a simple sample
    while(1){
        ret = read(fd, &c, 1);
        if(-1 == ret){
            perror("GetDllSymbol read\n");
            return 1;
        }
        if(c == '^'){
            break;
        }
    }

    while(1){
        ret = read(fd, &c, 1);
        if(-1 == ret){
            perror("GetDllSymbol read\n");
            return 1;
        }
        if(c == '^'){
            break;
        }else {
            *pBuf = c;
            pBuf++;
        }
    }

    *pBuf = '\0';

    return 0;
}

int AddDllSymbolToList(char *buf)
{
    int bufLen;
    char *data;
    bufLen = strlen(buf)+1;

    data = (char *)malloc(bufLen);
    if(NULL == data){
        printf("Error: AddDllSymbolToList failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return 1;
    }

    dll_symbol_list *symbol = (dll_symbol_list *)malloc(sizeof(dll_symbol_list));
    if (NULL == symbol) {
        free(data);
        return 1;
    }

    strcpy(data, buf);

    symbol->data = data;
    //todo:lock
    dll_symbol_list_add_head(&g_dllSymbolListHead, symbol);

    return 0;
}

int GetDllInfoThread(void *pArg)
{
    int fd;
    char buf[256];
    fd = (int)pArg;

    while(1){
        if(!GetDllSymbol(fd, buf)){
            AddDllSymbolToList(buf);
        }
    }

    return 0;
}
